
#include <cmath>
#include "IRound.h"

using namespace BWE;

void IRound::createTriangles(const BPointfArray& pointsA, const BPointfArray& pointsB, BPointfArray& points)
{
	points.reset();
	if (pointsB.size() == 1)
	{
		return;
	}
	int indexA = 0;
	int indexB = 0;
	int sizeA = pointsA.size() - 1;
	int sizeB = pointsB.size() - 1;
	while (indexA < sizeA || indexB < sizeB)
	{
		points.append(pointsA[indexA]);
		points.append(pointsB[indexB]);
		if (indexA == sizeA)
		{
			points.append(pointsB[++indexB]);
			break;
		}
		if (indexB == sizeB)
		{
			points.append(pointsA[++indexA]);
			break;
		}
		float lenA2 = (pointsA[indexA + 1] - pointsB[indexB]).length2();
		float lenB2 = (pointsB[indexB + 1] - pointsA[indexA]).length2();
		if (lenA2 < lenB2)
		{
			points.append(pointsA[++indexA]);
		}
		else
		{
			points.append(pointsB[++indexB]);
		}
	}
}
void IRound::createTriangles(const BPointfArray& pointsA, const BPointf& center, BPointfArray& points)
{
	points.reset();
	int sizeA = pointsA.size() - 1;
	for (int i = 0; i < sizeA; i++)
	{
		points.append(center);
		points.append(pointsA[i]);
		points.append(pointsA[i + 1]);
	}
	return;
}

IRound::IRound()
{
	halfPI = PIf * 0.5f;
}
IRound::~IRound()
{

}

void IRound::reset()
{
	float radiusWidth = bMax(radiusLeftTop.width() + radiusRightTop.width(), radiusLeftBottom.width() + radiusRightBottom.width());
	float radiusHeight = bMax(radiusLeftTop.height() + radiusLeftBottom.height(), radiusRightTop.height() + radiusRightBottom.height());
	if (frameSize.width() < radiusWidth || frameSize.height() < radiusHeight)
	{
		float scale = bMin(frameSize.width() / radiusWidth, frameSize.height() / radiusHeight);
		radiusLeftTop *= scale;
		radiusLeftBottom *= scale;
		radiusRightTop *= scale;
		radiusRightBottom *= scale;
	}
	centerLeftTop.set(radiusLeftTop.width(), radiusLeftTop.height());
	centerRightTop.set(frameSize.width() - radiusRightTop.width(), radiusRightTop.height());
	centerLeftBottom.set(radiusLeftBottom.width(), frameSize.height() - radiusLeftBottom.height());
	centerRightBottom.set(frameSize.width() - radiusRightBottom.width(), frameSize.height() - radiusRightBottom.height());
}
void IRound::movePoints(const BPoint& offset)
{
	for (int i = 0; i < titlePoints.size(); i++)
		titlePoints[i] += offset;

	for (int i = 0; i < borderPoints.size(); i++)
		borderPoints[i] += offset;

	for (int i = 0; i < perchPoints.size(); i++)
		perchPoints[i] += offset;

	for (int i = 0; i < marginPoints.size(); i++)
		marginPoints[i] += offset;

	for (int i = 0; i < centerPoints.size(); i++)
		centerPoints[i] += offset;

	for (int i = 0; i < backgroundPoints.size(); i++)
		backgroundPoints[i] += offset;
}
void IRound::preferSize(const BRectf& rect, BSizef& size)
{
	float leftTopX = radiusLeftTop.width() - rect.left();
	float leftTopY = radiusLeftTop.height() - rect.top();

	float leftBottomX = radiusLeftBottom.width() - rect.left();
	float leftBottomY = radiusLeftBottom.height() - (frameSize.height() - rect.bottom());

	float rightTopX = radiusRightTop.width() - (frameSize.width() - rect.right());
	float rightTopY = radiusRightTop.height() - rect.top();

	float rightBottomX = radiusRightBottom.width() - (frameSize.width() - rect.right());
	float rightBottomY = radiusRightBottom.height() - (frameSize.height() - rect.bottom());

	size.width() = bMax(leftTopX, leftBottomX) + bMax(rightTopX, rightBottomX);
	size.height() = bMax(leftTopY, rightTopY) + bMax(leftBottomY, rightBottomY);
}

bool IRound::containPoint(BPointf point)
{
	if (point.x() < 0 || point.x() > frameSize.width())
		return false;
	if (point.y() < 0 || point.y() > frameSize.height())
		return false;

	BRectf leftTopRect;
	leftTopRect.right() = radiusLeftTop.width();
	leftTopRect.bottom() = radiusLeftTop.height();
	if (leftTopRect.contain(point))
	{
		float dx = point.x() - centerLeftTop.x();
		float dy = point.y() - centerLeftTop.y();
		float rx = radiusLeftTop.width();
		float ry = radiusLeftTop.height();
		float value = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry);
		return value <= 1.0f;
	}

	BRectf rightTopRect;
	rightTopRect.left() = centerRightTop.x();
	rightTopRect.right() = frameSize.width();
	rightTopRect.bottom() = radiusRightTop.height();
	if (rightTopRect.contain(point))
	{
		float dx = point.x() - centerRightTop.x();
		float dy = point.y() - centerRightTop.y();
		float rx = radiusRightTop.width();
		float ry = radiusRightTop.height();
		float value = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry);
		return value <= 1.0f;
	}

	BRectf rectLeftBottom;
	rectLeftBottom.top() = centerLeftBottom.y();
	rectLeftBottom.right() = radiusLeftBottom.width();
	rectLeftBottom.bottom() = frameSize.height();
	if (rectLeftBottom.contain(point))
	{
		float dx = point.x() - centerLeftBottom.x();
		float dy = point.y() - centerLeftBottom.y();
		float rx = radiusLeftBottom.width();
		float ry = radiusLeftBottom.height();
		float value = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry);
		return value <= 1.0f;
	}

	BRectf rectRightBottom;
	rectRightBottom.left() = centerRightBottom.x();
	rectRightBottom.top() = centerRightBottom.y();
	rectRightBottom.right() = frameSize.width();
	rectRightBottom.bottom() = frameSize.height();
	if (rectRightBottom.contain(point))
	{
		float dx = point.x() - centerRightBottom.x();
		float dy = point.y() - centerRightBottom.y();
		float rx = radiusRightBottom.width();
		float ry = radiusRightBottom.height();
		float value = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry);
		return value <= 1.0f;
	}

	return true;
}

void IRound::createPoints(const BRectf& rect, BPointfArray& points)
{
	points.reset();
	createLeftTopPoints(rect, points);
	createRightTopPoints(rect, points);
	createRightBottomPoints(rect, points);
	createLeftBottomPoints(rect, points);
	if (points.size())
	{
		points.append(points.first());
	}
}
void IRound::createLeftTopPoints(const BRectf& rect, BPointfArray& points)
{
	float radiusX = radiusLeftTop.width() - rect.left();
	float radiusY = radiusLeftTop.height() - rect.top();
	if (radiusX <= 0 || radiusY <= 0)
	{
		points.append(rect.leftTop());
	}
	else
	{
		if (radiusX == radiusY)
		{
			int sides = (int)bClamp(radiusX / 3, 3, 24);
			float sr = halfPI / sides;
			for (int i = 0; i <= sides; i++)
			{
				float angle = PIf - i * sr;
				float x = cos(angle) * radiusX;
				float y = sin(angle) * radiusX;
				points.append(centerLeftTop + BPointf(x, -y));
			}
		}
		else
		{
			float rx2 = radiusX * radiusX;
			float ry2 = radiusY * radiusY;
			for (float x = -radiusX; x < 0; x += 2)
			{
				float y = sqrt((1.0f - (x * x) / rx2) * ry2);
				points.append(centerLeftTop + BPointf(x, -y));
			}
			points.append(centerLeftTop + BPointf(0, -radiusY));
		}
	}
}
void IRound::createLeftBottomPoints(const BRectf& rect, BPointfArray& points)
{
	float radiusX = radiusLeftBottom.width() - rect.left();
	float radiusY = radiusLeftBottom.height() - (frameSize.height() - rect.bottom());
	if (radiusX <= 0 || radiusY <= 0)
	{
		points.append(rect.leftBottom());
	}
	else
	{
		if (radiusX == radiusY)
		{
			int sides = (int)bClamp(radiusX / 3, 3, 24);
			float sr = halfPI / sides;
			for (int i = 0; i <= sides; i++)
			{
				float angle = -(halfPI + i * sr);
				float x = cos(angle) * radiusX;
				float y = sin(angle) * radiusX;
				points.append(centerLeftBottom + BPointf(x, -y));
			}
		}
		else
		{
			float rx2 = radiusX * radiusX;
			float ry2 = radiusY * radiusY;
			for (float x = 0; x > -radiusX; x -= 2)
			{
				float y = sqrt((1 - (x * x) / rx2) * ry2);
				points.append(centerLeftBottom + BPointf(x, y));
			}
			points.append(centerLeftBottom + BPointf(-radiusX, 0));
		}
	}
}
void IRound::createRightTopPoints(const BRectf& rect, BPointfArray& points)
{
	float radiusX = radiusRightTop.width() - (frameSize.width() - rect.right());
	float radiusY = radiusRightTop.height() - rect.top();
	if (radiusX <= 0 || radiusY <= 0)
	{
		points.append(rect.rightTop());
	}
	else
	{
		if (radiusX == radiusY)
		{
			int sides = (int)bClamp(radiusX / 3, 3, 24);
			float sr = halfPI / sides;
			for (int i = 0; i <= sides; i++)
			{
				float angle = halfPI - i * sr;
				float x = cos(angle) * radiusX;
				float y = sin(angle) * radiusX;
				points.append(centerRightTop + BPointf(x, -y));
			}
		}
		else
		{
			float rx2 = radiusX * radiusX;
			float ry2 = radiusY * radiusY;
			for (float x = 0; x < radiusX; x += 2)
			{
				float y = sqrt((1.0f - (x * x) / rx2) * ry2);
				points.append(centerRightTop + BPointf(x, -y));
			}
			points.append(centerRightTop + BPointf(radiusX, 0));
		}
	}
}
void IRound::createRightBottomPoints(const BRectf& rect, BPointfArray& points)
{
	float radiusX = radiusRightBottom.width() - (frameSize.width() - rect.right());
	float radiusY = radiusRightBottom.height() - (frameSize.height() - rect.bottom());
	if (radiusX <= 0 || radiusY <= 0)
	{
		points.append(rect.rightBottom());
	}
	else
	{
		if (radiusX == radiusY)
		{
			int sides = (int)bClamp(radiusX / 3, 3, 24);
			float sr = halfPI / sides;
			for (int i = 0; i <= sides; i++)
			{
				float angle = -i * sr;
				float x = cos(angle) * radiusX;
				float y = sin(angle) * radiusX;
				points.append(centerRightBottom + BPointf(x, -y));
			}
		}
		else
		{
			float rx2 = radiusX * radiusX;
			float ry2 = radiusY * radiusY;
			for (float x = radiusX; x > 0; x -= 2)
			{
				float y = sqrt((1 - (x * x) / rx2) * ry2);
				points.append(centerRightBottom + BPointf(x, y));
			}
			points.append(centerRightBottom + BPointf(0, radiusY));
		}
	}
}
void IRound::createInnerPoints(BRectf rect, BRectf innerRect, BPointfArray& points)
{
	points.reset();

	float leftTopX = radiusLeftTop.width() - rect.left();
	float leftTopY = radiusLeftTop.height() - rect.top();

	float leftBottomX = radiusLeftBottom.width() - rect.left();
	float leftBottomY = radiusLeftBottom.height() - (frameSize.height() - rect.bottom());

	float rightTopX = radiusRightTop.width() - (frameSize.width() - rect.right());
	float rightTopY = radiusRightTop.height() - rect.top();

	float rightBottomX = radiusRightBottom.width() - (frameSize.width() - rect.right());
	float rightBottomY = radiusRightBottom.height() - (frameSize.height() - rect.bottom());

	float width = bMax(leftTopX + rightTopX, leftBottomX + rightBottomX);
	float height = bMax(leftTopY + leftBottomY, rightTopY + rightBottomY);
	if (width > innerRect.width() || height > innerRect.height())
	{
		float scale = 1 - bMin(innerRect.width() / width, innerRect.height() / height);
		rect.top() += bMax(leftTopY, rightTopY) * scale;
		rect.left() += bMax(leftTopX, leftBottomX) * scale;
		rect.right() -= bMax(rightTopX, rightBottomX) * scale;
		rect.bottom() -= bMax(leftBottomY, rightBottomY) * scale;
	}

	int begin = 0;
	createLeftTopPoints(rect, points);
	{
		BPointf offset = innerRect.leftTop() - rect.leftTop();
		for (int i = begin; i < points.size(); i++)
			points[i] += offset;
	}

	begin = points.size();
	createRightTopPoints(rect, points);
	{
		BPointf offset = innerRect.rightTop() - rect.rightTop();
		for (int i = begin; i < points.size(); i++)
			points[i] += offset;
	}

	begin = points.size();
	createRightBottomPoints(rect, points);
	{
		BPointf offset = innerRect.rightBottom() - rect.rightBottom();
		for (int i = begin; i < points.size(); i++)
			points[i] += offset;
	}

	begin = points.size();
	createLeftBottomPoints(rect, points);
	{
		BPointf offset = innerRect.leftBottom() - rect.leftBottom();
		for (int i = begin; i < points.size(); i++)
			points[i] += offset;
	}

	if (points.size())
	{
		points.append(points.first());
	}

}

